/**********************************************************************************
 *  jcw - this is a environment for solving japan crosswords by users on computer *
 *  Copyright (C) 2008 by pluton <plutonpluton@mail.ru>                           *
 *                                                                                *
 *  This program is free software; you can redistribute it and/or modify          *
 *  it under the terms of the GNU General Public License as published by          *
 *  the Free Software Foundation; either version 2 of the License, or             *
 *  (at your option) any later version.                                           *
 *                                                                                *
 *  This program is distributed in the hope that it will be useful,               *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of                *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
 *  GNU General Public License for more details.                                  *
 *                                                                                *
 *  You should have received a copy of the GNU General Public License along       *
 *  with this program; if not, write to the Free Software Foundation, Inc.,       *
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.                   *
 *********************************************************************************/

#include "fieldcheckerthread.h"

FieldCheckerThread::FieldCheckerThread(QWidget* parent)
	: QThread(parent)
{
	fw = fh = th = lw = 0;
	crossword = NULL;
}

void FieldCheckerThread::run()
{
}

void FieldCheckerThread::check(quint16 fX, quint16 fY)
{
	if (cwMode != CWType::SolvingCrossword)
		return;

	if (fX == quint16(-1) && fY == quint16(-1))			// если координаты == -1, то надо обновить весь прогресс
	{
		updateProgress();
		return;
	}
	else if (fX == quint16(-2) && fY == quint16(-2))	// если координаты == -2, то надо сбросить прогресс
	{
		emit resultTextChanged(tr("ready"));
		emit progressChanged(0);
		return;
	}

	/* преобразовываем строку поля в текст */
	QString row(fw, '_');
	quint16 i;
	for (i = 0; i < fw; i++)
		switch (field[i][fY])
		{
			case ClassicCW::csUndef:
				row[i] = '_';
				break;
			case ClassicCW::csEmpty:
				row[i] = '.';
				break;
			case ClassicCW::csFilled:
				row[i] = '*';
				break;
		}
	/* нужно составить паттерн регэкспа для проверки */
	QString rowPattern("^[_.]*");		// <- это только начало :)
	for (i = 0; i < ((ClassicCW*) crossword)->getGroupCnt(false, fY); i++)
	{
		quint16 num = qAbs(lhdr[fY][i]);
		if (num == 0)					// останавливаемся на нуле
			break;
		if (i != 0)						// каждый раз, кроме первого, добавляем разделитель блоков
			rowPattern.append("[_.]+");
		rowPattern.append(QString("\\*{%1}").arg(num));
	}
	rowPattern.append("[_.]*$");		// <- конец регэкспа

	/* преобразовываем столбец поля в текст */
	QString col(fh, '_');
	for (i = 0; i < fh; i++)
		switch (field[fX][i])
		{
			case ClassicCW::csUndef:
				col[i] = '_';
				break;
			case ClassicCW::csEmpty:
				col[i] = '.';
				break;
			case ClassicCW::csFilled:
				col[i] = '*';
				break;
		}
	QString colPattern("^[_.]*");
	for (i = 0; i < ((ClassicCW*) crossword)->getGroupCnt(true, fX); i++)
	{
		quint16 num = qAbs(thdr[fX][i]);
		if (num == 0)
			break;
		if (i != 0)
			colPattern.append("[_.]+");
		colPattern.append(QString("\\*{%1}").arg(num));
	}
	colPattern.append("[_.]*$");		// <- конец регэкспа

	rowsRes->setBit(fY, row.contains(QRegExp(rowPattern)));
	colsRes->setBit(fX, col.contains(QRegExp(colPattern)));

	emit resultTextChanged(QString(tr("row #%1: %2; col #%3: %4")).
			arg(fY + 1).arg(rowsRes->at(fY) ? tr("ok") : tr("bad")).
			arg(fX + 1).arg(colsRes->at(fX) ? tr("ok") : tr("bad"))); //???

	/* посчитаем прогресс решения */
	quint16 validLines = 0;
	/* и проверяем, полностью ли решён кроссворд */
	bool isSolved = true;
	for (i = 0; i < fh; i++)
		if (!rowsRes->at(i))
			isSolved = false;
		else
			validLines++;
	for (i = 0; i < fw; i++)
		if (!colsRes->at(i))
			isSolved = false;
		else
			validLines++;

	emit progressChanged(validLines * 100 / (fw + fh));
	if (isSolved)
		emit solved();
}

void FieldCheckerThread::setCrossword(AbstractCrossword* crossword, CWType::CrosswordType type, CWType::CrosswordMode cwMode)
{
	//qDebug() << crossword->getName();
	this->crossword = crossword;
	this->cwMode = cwMode;
	cwType = type;
	if (cwType == CWType::Classic)
	{
		connect(crossword, SIGNAL(cellStateChanged(quint16, quint16)), this, SLOT(check(quint16, quint16)));
		connect(crossword, SIGNAL(checkHeaders()), this, SLOT(checkSums()));
	}
	else if (cwType == CWType::Color)
	{

	}
	((RectCrossword*) crossword)->sizes(&fw, &fh, &th, &lw);
	thdr = ((ClassicCW*) crossword)->getThdr();
	lhdr = ((ClassicCW*) crossword)->getLhdr();
	field = ((ClassicCW*) crossword)->getField();
	rowsRes = ((RectCrossword*) crossword)->getRowsRes();
	colsRes = ((RectCrossword*) crossword)->getColsRes();
	if (cwMode == CWType::FillUpHeaders)
		checkSums();
}

void FieldCheckerThread::updateProgress()
{
	quint16 i;
	for (i = 0; i < ((fw > fh) ? fw : fh); i++)
		check(((i >= fw) ? 0 : i), ((i >= fh) ? 0 : i));

	emit resultTextChanged(tr("ready"));

	quint16 validLines = 0;
	bool isSolved = true;
	for (i = 0; i < fh; i++)
		if (!rowsRes->at(i))
			isSolved = false;
	else
		validLines++;
	for (i = 0; i < fw; i++)
		if (!colsRes->at(i))
			isSolved = false;
	else
		validLines++;

	emit progressChanged(validLines * 100 / (fw + fh));
	if (isSolved)
		emit solved();
}

void FieldCheckerThread::checkSums()
{
	if (cwMode != CWType::FillUpHeaders)
		return;

	QString resultC = "", resultR = "";
	quint16 i, j, summTh = 0, summLh = 0, currSumm;
	for (i = 0; i < fw; i++)
	{
		currSumm = 0;
		for (j = 0; j < th; j++)
		{
			summTh += thdr[i][j];
			currSumm += ((currSumm != 0 && thdr[i][j] != 0) ? 1 : 0) + thdr[i][j];
		}
		if (resultC.length() == 0 && currSumm > fh)
			resultC = tr(", Group summ in column #%1 greater than field height").arg(i + 1);
	}

	for (i = 0; i < fh; i++)
	{
		currSumm = 0;
		for (j = 0; j < lw; j++)
		{
			summLh += lhdr[i][j];
			currSumm += ((currSumm != 0 && lhdr[i][j] != 0) ? 1 : 0) + lhdr[i][j];
		}
		if (resultR.length() == 0 && currSumm > fw)
			resultR = tr(", Group summ in row #%1 greater than field width").arg(i + 1);
	}

	emit resultTextChanged(tr("Columns summ = %1, Rows summ = %2").arg(summTh).arg(summLh) + resultC + resultR);
}
